/***
 * KC031 GPIO操作函数
 * 
 * KC031的GPIO的proc地址如下：
 * 		/sys/class/aexgpio/aexgpio27/value
 * 
 * TCA9535  P0~P7 为输入
 * 			P10~P17 为输出
 * */
#include <stdio.h>
#include <stdlib.h>

#include "aexgpio.h"
#include "utils.h"


/*
	KC031,IIC转GPIO引脚说明

	TCA9535芯片：
	TCA芯片-->功能-->PCB定义
	P00-->GPIO_IN_P0-->IN_P0 (J1插件输入)
	......
	P07-->GPIO_IN_P7-->IN_P7

	P10-->GPIO_OUT_P0-->J30 （继电器输出）
	...
	P17-->GPIO_OUT_P7-->J37
*/


/**
 * @brief 获取KC031输入引脚的状态，引脚序号已调整为从0开始。
 * 
 * @return unsigned long 
 */
unsigned long aexgpio_get_in()
{
	unsigned long ret = aexgpio_getvalue(DEFAULT_GPIO_ADDR);

	return (ret&AEXIN_MASK()) >> AEXIN_MIN_INDEX;
}

/**
 * @brief 单引脚获得引脚状态，高低电平
 * 
 * @param index 从0开始
 * @return unsigned long 
 */
unsigned long aexgpio_get_in_by_index(int index)
{
	unsigned long ret = aexgpio_get_in();

	return !!(ret&(1<<index));
}

/**
 * @brief 获取KC031输出引脚的状态，已调整为从0开始
 * 
 * @return unsigned long  
 */
unsigned long aexgpio_get_out()
{
	unsigned long ret = aexgpio_getvalue(DEFAULT_GPIO_ADDR);

	return (ret&AEXOUT_MASK()) >> AEXOUT_MIN_INDEX;
}

/**
 * @brief 根据index获取输出引脚状态
 * 
 * @param index 从0开始
 * @return unsigned long 
 */
unsigned long aexgpio_get_out_by_index(int index)
{
	unsigned long ret = aexgpio_get_out();

	return !!(ret&(1<<index));
}

/**
 * @brief 设置输出引脚的电平状态
 * 	根据msk来设置，mask为0的位不改变原来的状态。
 * 
 * @param mask ,输出位掩码：0xff00
 * @param value 0xxx00
 */
void aexgpio_set_out(unsigned long mask,unsigned long value)
{
	unsigned int ret =  aexgpio_getvalue(DEFAULT_GPIO_ADDR);

	// psus_hint("-%s(%04X,0x%04X),0x%04X",__func__, mask,value,ret);
	mask <<= AEXOUT_MIN_INDEX;
	value <<= AEXOUT_MIN_INDEX;
	ret &= ~mask;
	value &= mask;

	ret  |= value;

	// psus_hint("-%s(0x%04X,0x%04X",__func__ ,mask,ret);
	aexgpio_setvalue(DEFAULT_GPIO_ADDR,ret); 
}

//mask 0xff
void aexgpio_set_out_invert(unsigned long mask)
{
	unsigned long ret =  aexgpio_getvalue(DEFAULT_GPIO_ADDR);

	mask <<= AEXOUT_MIN_INDEX;
	ret ^= mask;
	aexgpio_setvalue(DEFAULT_GPIO_ADDR,ret); 
}

/**
 * @brief 设置单引脚的输出状态
 * 
 * @param index 
 * @param level 
 */
void aexgpio_set_out_by_index(unsigned int index,unsigned int level)
{
	aexgpio_set(DEFAULT_GPIO_ADDR,index,level); 
}

/**
 * 计算 n 位需要多少个字节存储
 */
unsigned char bits_to_bytes(unsigned char n_bits) 
{
    return (n_bits >> 3) + ((n_bits & 0x07) ? 1 : 0);
}

/**
 * 这个函数 get_pdata_bit 的作用是将一个无符号长整型 value 的
 * 二进制位（从最高位到最低位）逐个提取，并以逗号分隔的形式存入字
 * 符串缓冲区 wbuf 中。
 */
char *get_pdata_bit(char *wbuf,int wlen, unsigned char *bitv, int count) 
{
	unsigned long value = 0;
	int bitc = bits_to_bytes(count);

    if (count <= 0 || !wbuf) return wbuf; // 安全检查

	for(int i = 0;i<bitc;i++){
		value <<= 8;			
		value |= bitv[i];
	}
    
    for (int i = 0; i < count; i++) {
        int bit = (value >> (count - 1 - i)) & 1; // 从高位到低位提取
        if (i == 0) {
            snprintf(wbuf,wlen, "%d", bit);
        } else {
            snprintf(wbuf + strlen(wbuf), wlen,",%d", bit);
        }
    }
    return wbuf;
}

/**
 * @brief 将大端序的 16 位无符号整数数组转换为逗号分隔的字符串
 * @param buffer 输入的无符号整数数组（大端序）
 * @param buf_size 数组长度（元素个数）
 * @param output 输出的字符串缓冲区
 * @param output_size 输出缓冲区大小（防止溢出）
 * @return 成功返回 output，失败返回 NULL
 */
char* uint32_array_to_csv(const uint8_t* buffer, size_t buf_size, char* output, size_t output_size) 
{
    if (!buffer || !output || buf_size == 0 || output_size == 0) {
        return ""; // 参数检查
    }

    size_t pos = 0; // 当前写入位置
    for (size_t i = 0; i < buf_size; i++) {
        uint32_t value = 0;
        for(int j=0;j<AEXGPIO_MAX_AIO_BYTE;j++){
            value <<= 8;
            value |= buffer[i*AEXGPIO_MAX_AIO_BYTE+j];
        }

        // 计算需要的空间（数字 + 逗号/结束符）
        int written;
        if (i == buf_size - 1) {
            written = snprintf(output + pos, output_size - pos, "%u", value);
        } else {
            written = snprintf(output + pos, output_size - pos, "%u,", value);
        }

        if (written < 0 || (size_t)written >= output_size - pos) {
            return "\"overflow\""; // 缓冲区不足或写入失败
        }
        pos += written;
    }

    return output;
}

/**
 * 获取可打印缓冲区，如果不是可打印字符串，则返回空字符串，否则返回可打印字符串
 */
char *get_utf8_printable(const unsigned char *buf, size_t size) 
{
    for (size_t i = 0; i < size; ) {
        if (buf[i] == '\0') {
            i++;
            continue;
        }

        // 检查单字节字符（ASCII）
        if (buf[i] <= 0x7F) {
            if (!isprint(buf[i])) return "";
            i++;
        } 
        // 检查多字节字符（UTF-8 编码）
        else {
            int len = 0;
            if ((buf[i] & 0xE0) == 0xC0) len = 2;     // 2字节
            else if ((buf[i] & 0xF0) == 0xE0) len = 3; // 3字节
            else if ((buf[i] & 0xF8) == 0xF0) len = 4; // 4字节
            else return ""; // 非法 UTF-8 起始字节

            if (i + len > size) return ""; // 超出缓冲区

            // 检查后续字节是否为 10xxxxxx
            for (int j = 1; j < len; j++) {
                if ((buf[i + j] & 0xC0) != 0x80) {
                    return "";
                }
            }
            i += len;
        }
    }
    return buf;
}

unsigned char aexgpio_set_pdata_dio(unsigned char *pdata,int index,unsigned char num,unsigned long value)
{
	int i;

	pdata[index++] = num;		//模块的开关量输出数量
    // psus_hint("DIO NUM=%d",num);
	for(i=bits_to_bytes(num)-1;i>=0;i--){
        pdata[index++] = (value>>(i*8))&0xFF;
	}
    return index;
}

unsigned char aexgpio_set_pdata_ai(unsigned char addr,unsigned char *pdata,int index,unsigned char num)
{
	int i;

	pdata[index++] = num;		//模块的开关量输出数量
	for(i=0;i<num;i++){
        unsigned long value = aexgpio_get_ai(addr,i);
        pdata[index++] = (value>>24)&0xFF;
        pdata[index++] = (value>>16)&0xFF;
        pdata[index++] = (value>>8)&0xFF;
        pdata[index++] = (value>>0)&0xFF;
	}
    return index;
}

unsigned char aexgpio_set_pdata_ao(unsigned char addr,unsigned char *pdata,int index,unsigned char num)
{
	int i;

	pdata[index++] = num;		//模块的开关量输出数量
	for(i=0;i<num;i++){
        unsigned long value = aexgpio_get_ao(addr,i);
        pdata[index++] = (value>>24)&0xFF;
        pdata[index++] = (value>>16)&0xFF;
        pdata[index++] = (value>>8)&0xFF;
        pdata[index++] = (value>>0)&0xFF;
	}
    return index;
}
